using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Framework.Timer
{
    /// <summary>
    /// //对全局提供Update服务
    /// </summary>
    public sealed class GameUpdateMgr : MonoBehaviour
    {

        #region Debug

        [SerializeField]
        private int UpdateCount;

        [SerializeField]
        private int FixedUpdateCount;

        [SerializeField]
        private int CoroutineCount;

        [SerializeField]
        private int TimerCount;

        #endregion


        public enum UpdaterType
        {

            Update,
            FixedUpdate,
            Coroutine

        }

        private static bool          mIsInit = false;
        private static GameUpdateMgr _instance;

        private const int _DEFAULT_LENGTH = 1024;
        
        public static GameUpdateMgr Instance
        {
            get
            {
                if (_instance == null && !mIsInit)
                {
                    var objs = FindObjectsOfType<GameUpdateMgr>();
                    for (int i = 0; i < objs.Length; i++)
                    {
                        Destroy(objs[i]);
                    }

                    _instance      = new GameObject().AddComponent<GameUpdateMgr>();
                    _instance.name = "[GameUpdater]";
                    DontDestroyOnLoad(_instance);
                    _instance.Init();
                    mIsInit = true;
                }

                return _instance;
            }
            private set { _instance = value; }
        }

        private List<Action> _UpdateList;
        private List<Action> _FixedUpdateList;
        private List<Action> _CoroutineList;

        private List<TimeHandler> _TimeHandlers;

        private void Init()
        {
            this._UpdateList      = new List<Action>(_DEFAULT_LENGTH);
            this._FixedUpdateList = new List<Action>(_DEFAULT_LENGTH);
            this._CoroutineList   = new List<Action>(_DEFAULT_LENGTH);
            this._TimeHandlers    = new List<TimeHandler>(_DEFAULT_LENGTH);
            this.StartCoroutine(this.UpdateCoroutine());
            this.StartCoroutine(this.UpdateTimer());
        }

        private void _InvokeUpdate(List<Action> list)
        {
            for (int i = list.Count - 1; i >= 0; i--)
            {
                //在遍历时可能会出现外部操作list导致Count改变
                if (i >= list.Count)
                    continue;
                if (list[i] == null)
                {
                    list.RemoveAt(i);
                }
                else
                {
                    list[i].Invoke();
                }
            }
        }

        private void _UpdateCount()
        {
            this.UpdateCount      = this._UpdateList.Count;
            this.FixedUpdateCount = this._FixedUpdateList.Count;
            this.CoroutineCount   = this._CoroutineList.Count;
            this.TimerCount       = this._TimeHandlers.Count;
        }

        #region Updater

        private void Update()      => this._InvokeUpdate(this._UpdateList);
        private void FixedUpdate() => this._InvokeUpdate(this._FixedUpdateList);

        private IEnumerator UpdateCoroutine()
        {
            while (true)
            {
                this._InvokeUpdate(this._CoroutineList);
                yield return null;
            }
        }

        private IEnumerator UpdateTimer()
        {
            while (true)
            {
#if UNITY_EDITOR
                this.TimerCount = this._TimeHandlers.Count;
#endif
                for (int i = this._TimeHandlers.Count - 1; i >= 0; i--)
                {
                    if (this._TimeHandlers[i]?.IsDone ?? true)
                    {
                        this._TimeHandlers.RemoveAt(i);
                    }
                    else
                    {
                        this._TimeHandlers[i].Update();
                    }
                }

                yield return null;
            }
        }

        #endregion

        public TimeHandler CreateTimer(float delay, Action callback, Action<float> update = null)
        {
            if (delay <= 0)
            {
                callback.Invoke();
                return null;
            }
            var timer = new TimeHandler(delay, callback, update);
            this._TimeHandlers.Add(timer);
            return timer;
        }

        public void AddUpdater(Action updater, UpdaterType updaterType = UpdaterType.Update)
        {
            switch (updaterType)
            {
                case UpdaterType.Update:
                    this._UpdateList.Add(updater);
                    break;
                case UpdaterType.FixedUpdate:
                    this._FixedUpdateList.Add(updater);
                    break;
                case UpdaterType.Coroutine:
                    this._CoroutineList.Add(updater);
                    break;
            }
#if UNITY_EDITOR
            this._UpdateCount();
#endif
        }

        public void RemoveUpdater(Action updater, UpdaterType updaterType = UpdaterType.Update)
        {
            switch (updaterType)
            {
                case UpdaterType.Update:
                    this._UpdateList.Remove(updater);
                    break;
                case UpdaterType.FixedUpdate:
                    this._FixedUpdateList.Remove(updater);
                    break;
                case UpdaterType.Coroutine:
                    this._CoroutineList.Remove(updater);
                    break;
            }
#if UNITY_EDITOR
            this._UpdateCount();
#endif
        }

    }
}